Imports Microsoft.DirectX
Imports Microsoft.DirectX.DirectSound

' Represents a sound format for a specific capture device.
Friend Class SoundFormat

    ' Capture device for which this sound format has been created and validated.
    Private _captureDevice As Capture
    ' Constructed format for wave file.
    Private _currentFormat As WaveFormat

    ' Number of channels (stereo sound would be equal to 2).
    Private _channels As Short = 0
    ' Number of bits used to represent each sample taken.
    Private _bitsPerSample As Short = 0
    ' Number of samples to take per second of recording time.
    Private _samplesPerSecond As Integer = 0

    ' Simple constructor takes a Capture instance and assumes some default
    ' values for channels, bits, samples.
    Public Sub New(ByVal captureDevice As Capture)
        Me.New(captureDevice, SampleRate.Rate11KHz, SampleSize.Bits8, 1)
    End Sub

    ' Full constructor takes Capture instance and specific values for
    ' channels, bits, and samples.
    Friend Sub New(ByVal captureDevice As Capture, ByVal rate As SampleRate, ByVal size As SampleSize, ByVal channels As Short)
        If captureDevice Is Nothing Then
            Throw New ArgumentNullException("captureDevice")
        End If

        Me._captureDevice = captureDevice

        Try
            ' Test the supplied format characteristics.
            Me._currentFormat = ConstructFormat(CInt(Fix(rate)), CShort(Fix(size)), CShort(Fix(channels)))
        Catch ex As Exception
            Dim errMsg As String = String.Format("Sound format not supported: {0} samples/sec, {1} bits/sample, {2} channels.", CInt(Fix(rate)), CShort(Fix(size)), CShort(Fix(channels)))
            Throw New Exception(errMsg, ex)
        End Try

        Me._channels = channels
        Me._bitsPerSample = CShort(Fix(size))
        Me._samplesPerSecond = CInt(Fix(rate))
    End Sub

    Friend ReadOnly Property Format() As WaveFormat
        Get
            Return Me._currentFormat
        End Get
    End Property

    Friend ReadOnly Property Channels() As Short
        Get
            Return Me._channels
        End Get
    End Property

    Friend ReadOnly Property BitsPerSample() As Short
        Get
            Return Me._bitsPerSample
        End Get
    End Property

    Friend ReadOnly Property SamplesPerSecond() As Integer
        Get
            Return Me._samplesPerSecond
        End Get
    End Property

    ' Helper method to test a specific combination of samples, bits, and channels
    ' against the capture device.
    Private Function ConstructFormat(ByVal samplesPerSecond As Integer, ByVal bitsPerSample As Short, ByVal channels As Short) As WaveFormat
        Dim newFormat As WaveFormat = New WaveFormat()

        newFormat.FormatTag = WaveFormatTag.Pcm
        newFormat.SamplesPerSecond = samplesPerSecond
        newFormat.BitsPerSample = bitsPerSample
        newFormat.Channels = channels
        newFormat.BlockAlign = CShort(Fix(channels * (bitsPerSample / 8)))
        newFormat.AverageBytesPerSecond = newFormat.BlockAlign * samplesPerSecond

        VerifyFormat(newFormat)

        Return newFormat
    End Function

    ' Helper method to test a specific WaveFormat instance.
    Private Sub VerifyFormat(ByVal newFormat As WaveFormat)
        If Me._captureDevice Is Nothing Then
            Throw New InvalidOperationException("Capture device is null.")
        End If

        Dim capBuffDesc As CaptureBufferDescription = New CaptureBufferDescription()
        capBuffDesc.BufferBytes = newFormat.AverageBytesPerSecond
        capBuffDesc.Format = newFormat

        Dim capBuff As CaptureBuffer = Nothing

        Try
            capBuff = New CaptureBuffer(capBuffDesc, Me._captureDevice)
        Catch ex As Exception
            Dim errMsg As String = String.Format("Sound format not supported: {0} samples/sec, {1} bits/sample, {2} channels.", newFormat.SamplesPerSecond, newFormat.BitsPerSample, newFormat.Channels)
            Throw New Exception(errMsg, ex)
        End Try

        If Not capBuff Is Nothing Then
            capBuff.Dispose()
            capBuff = Nothing
        End If
    End Sub

End Class
